//
// Created by hujin on 2020/11/8.
//
#include <map>
#include "code_synthesis.h"
#include "register.h"
#ifndef C_MIPS_CMPLR_MEM_ALLOCATOR_H
#define C_MIPS_CMPLR_MEM_ALLOCATOR_H



class MemoryAllocator{

    struct addr{
        bool isReg;
        bool isGlobal;
        int regId;
        int dataAddr;
        bool valChanged;
    };

    int stack_length = 0;
    int stack_length_freeze = 0;
    int param_cnt  = 0;
    int read_param_cnt  = 0;
    std::map<int,addr> data;
    std::map<int,int> redirect;
    int regCur[reg_cnt] = {};

    Synthesizer *synthesizer;
    Register *registerState;

    int requestReg(int desire, bool global);
    int stackMemAlloc(int size);
    int _loadToReg(int varid_old, bool write, bool notRead);
public:

    void setSynthesizer(Synthesizer *s);

    explicit MemoryAllocator(Synthesizer *synthesizer, Register *registerState)
            : synthesizer(synthesizer), registerState(registerState) {}


    explicit MemoryAllocator(Synthesizer *synthesizer, Register *registerState, MemoryAllocator *dataSource)
            : synthesizer(synthesizer), registerState(registerState) , data(dataSource->data){}

public:

    //varid must > 0
    void allocGlobalMem(int varid, int space);

    void allocGlobalMem(int varid, std::vector<int> initiation);

    void allocLocalArr(int varid, int space);

    //will not release variable in register regNum
    //before varid released:
    //read varid <-> read regNum <-> read regCur[varid]
    //write varid <-> write regNum <-> write regCur[varid]
    void setToSpecialReg(int varid, int regNum);  // lw

    //move is not write !
    int loadToReg(int varid, bool write);  // lw

    void loadToParam(int varid, int maxParamCnt);  // function call

    void readFromParam(int varid, int maxParamCnt) ;  // function init

    void move(int varids, int varidt); // s --> t

    void releaseReg(int varid);

    void save(int varid);  // sw

    int loadArrToReg(int varidarr, int varidTarget, int offset);

    void saveArr(int varidarr, int varid_source, int offset);

    // offsetVar = offsetVar + sp if not global
    int loadArrToRegVarPos(int varidarr, int varidTarget, int offsetVarid);

    void saveArrVarPos(int varidarr, int varid_source, int offsetVarid);
    void saveArrCnst(int varidarr, int value, int offset);
    void saveArrCnstVarPos(int varidarr, int value, int offsetVarid);

    MemoryAllocator* split();

    void mergeTo(MemoryAllocator *memNext, const std::set<int>& activeIds);

    void push_stack( );

    void pop_stack(); // TODO

    void moveToSpecialReg(int varid, int regNum);

    int transferData(int varid_old, int varid_new);

    virtual ~MemoryAllocator();

    void saveRegs(const std::set<int> &activeIds);

    void useReg(int Reg);

    void save_global_changes();

    int loadToReg_writeOnly(int varid_old);

};



#endif //C_MIPS_CMPLR_MEM_ALLOCATOR_H
